﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Reflection;
using System.Threading;
using System.Runtime.InteropServices;
using System.Text;
using System.Linq;
using Newtonsoft.Json;

namespace ModelSerialize
{
    public enum StreamType
    {
        Index,

        Data,
    }

    public interface IModelSerializeHelper
    {
        void Stop();
    }

    public class ModelSerializeHelper
    {
        private static ModelSerializeHelper _ModelSerializeHelper = null;
        private static object _lock = new object();
        private ReaderWriterLock _RWLock = new ReaderWriterLock();

        /// <summary>
        /// 保存路径
        /// </summary>
        public static string SavePath { get; set; } = "SavePath";

        /// <summary>
        /// 临时文件路径
        /// </summary>
        public static string TempPath { get; set; } = "TempPath";

        /// <summary>
        /// 时间戳
        /// </summary>
        public static long UnixTimestamp { get { return (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000; } }

        private List<IModelSerializeHelper> _ListModelSerializeHelper = new List<IModelSerializeHelper>();

        public static ModelSerializeHelper GetInstance()
        {
            if (_ModelSerializeHelper == null)
            {
                lock (_lock)
                {
                    _ModelSerializeHelper = new ModelSerializeHelper();
                }
            }

            return _ModelSerializeHelper;
        }

        public void Register(IModelSerializeHelper helper)
        {
            _RWLock.Write(() => { _ListModelSerializeHelper.Add(helper); });
        }

        public virtual void Stop()
        {
            _RWLock.Reader(() => { _ListModelSerializeHelper.ForEach(n => n.Stop()); });
        }

        /// <summary>
        /// 合并并返回临时文件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="start"></param>
        /// <param name="stop"></param>
        /// <returns></returns>
        public virtual TempFile MergeFile<T>(long start, long stop)
            where T : SerializeModelBase
        {
            var file = ModelSerializeHelper<T>.GetInstance().MergeFile(start, Math.Min(stop, ModelSerializeHelper.UnixTimestamp - 1));
            return new TempFile(file);
        }
    }

    public class ModelSerializeHelper<T> : IModelSerializeHelper
        where T : SerializeModelBase
    {
        private static ModelSerializeHelper<T> _ModelSerializeHelper = null;
        private static object _lock = new object();
        private AutoResetEvent NewModelResetEvent = new AutoResetEvent(false);                  // 新的数据加入事件
        private AutoResetEvent NewModelPushFinsh = new AutoResetEvent(true);                    // Modle压入流完成
        private ManualResetEvent MergeFinshResetEvent = new ManualResetEvent(true);                 // 合并数据结束事件
        private Thread MainThread = null;

        /// <summary>
        /// 索引流
        /// </summary>
        private Stream IndexStream = null;

        /// <summary>
        /// 数据流
        /// </summary>
        private Stream DataStream = null;

        ///// <summary>
        ///// 相关队列
        ///// </summary>
        //private ConcurrentStack<T> Stack = new ConcurrentStack<T>();

        private TheadStack<T> Stack = new TheadStack<T>();

        /// <summary>
        /// 间隔
        /// </summary>
        public const long TIME_INTERVAL = 60 * 10;
        //public const long TIME_INTERVAL = 60 * 60 * 24;
        //public const long TIME_INTERVAL = 3;

        /// <summary>
        /// 正在运行
        /// </summary>
        public bool IsRuning { get; private set; }

        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            IsRuning = false;
            MainThread?.Join();
        }

        /// <summary>
        /// 上次文件的时间戳
        /// </summary>
        public static long LastFileTimestamp = 0;

        /// <summary>
        /// 文件时间戳
        /// </summary>
        public static long FileTimestamp { get { return GetFileTimestamp(ModelSerializeHelper.UnixTimestamp); } }

        /// <summary>
        /// 事件间隔
        /// </summary>
        private const int EVENT_INTERVAL = 1000 * 2;

        private ModelSerializeHelper() { }

        public static ModelSerializeHelper<T> GetInstance()
        {
            if (_ModelSerializeHelper == null)
            {
                lock (_lock)
                {
                    if (_ModelSerializeHelper == null)
                    {
                        _ModelSerializeHelper = new ModelSerializeHelper<T>();
                        _ModelSerializeHelper.IsRuning = true;
                        _ModelSerializeHelper.MainThread = new Thread(_ModelSerializeHelper.OnMainThread) { IsBackground = true };
                        _ModelSerializeHelper.MainThread.Start();

                        ModelSerializeHelper.GetInstance().Register(_ModelSerializeHelper);
                    }
                }
            }

            return _ModelSerializeHelper;
        }

        /// <summary>
        /// 添加模型
        /// </summary>
        /// <param name="model"></param>
        public void Add(T model)
        {
            if (!IsRuning)
                return;

            model.Id = IdGenerator.GetInstance().Next();
            model.Timestamp = ModelSerializeHelper.UnixTimestamp;
            Stack.Push(model);
            NewModelResetEvent.Set();

            //Console.WriteLine("Model 加入队列");
        }

        /// <summary>
        /// 处理线程的相关逻辑
        /// </summary>
        private void OnMainThread()
        {
            while (IsRuning || !Stack.IsEmpty)
            {
                NewModelPushFinsh.Set();
                MergeFinshResetEvent.WaitOne();
                NewModelPushFinsh.Reset();

                NewModelResetEvent.WaitOne(EVENT_INTERVAL);

                if (Stack.IsEmpty)
                    continue;

                while (Stack.TryPeek(out T result))
                {
                    var file_timestamp = GetFileTimestamp(result.Timestamp);

                    // 检验流是否过期
                    if (file_timestamp != LastFileTimestamp)
                    {
                        Console.WriteLine("流文件过期或不匹配");

                        IndexStream?.Close();
                        IndexStream = null;

                        DataStream?.Close();
                        DataStream = null;
                    }

                    // 更新最近一次时间戳
                    LastFileTimestamp = file_timestamp;

                    // 网络连接不存在时处理连接
                    if (IndexStream == null)
                        IndexStream = GetIndexStream(file_timestamp);

                    if (DataStream == null)
                        DataStream = GetDataStream(file_timestamp);

                    Push(result);
                    Stack.TryPop(out T remove);
                }

                IndexStream?.Flush();
                DataStream?.Flush();
            }
        }

        /// <summary>
        /// 获取写入文件时的相关信息
        /// </summary>
        /// <returns></returns>
        private ModelFileInfo GetModelFileInfo()
        {
            Type type = typeof(T);
            ModelFileInfo file_info = new ModelFileInfo
            {
                Type = type,
            };

            long offset_sum = 0;
            foreach (var property in type.GetProperties())
            {
                ModelPropertyInfo mp_info = new ModelPropertyInfo();

                mp_info.Property = property;
                mp_info.Offset = offset_sum;
                mp_info.Length = StreamEx.GetSimplePackingTypeLength(property.PropertyType);

                offset_sum += mp_info.Length;

                file_info.PropertyInfos.Add(mp_info);
            }

            file_info.Length = offset_sum;
            return file_info;
        }

        /// <summary>
        /// 将模型转换为二进制数组
        /// （string 类型的数据将留空）
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        private byte[] GetModelByte(T model)
        {
            var file_info = GetModelFileInfo();
            byte[] data = new byte[file_info.Length];

            foreach (var property in file_info.PropertyInfos)
            {
                if (!property.Property.PropertyType.IsValueType)
                    continue;

                var property_data = StreamEx.GetByteBySimplePackingData(property.Property.GetValue(model));
                Array.Copy(property_data, 0, data, property.Offset, property.Length);
            }

            return data;
        }

        /// <summary>
        /// 将二进制转换为数据模型
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private T GetByteModel(byte[] data)
        {
            var file_info = GetModelFileInfo();
            T model = Activator.CreateInstance<T>();

            foreach (var property in file_info.PropertyInfos)
            {
                if (!property.Property.PropertyType.IsValueType)
                    continue;

                byte[] sub_byte = data.Sub(property.Offset, property.Length);
                var obj = StreamEx.GetSimplePackingDataByByte(sub_byte, property.Property.PropertyType);
                property.Property.SetValue(model, obj);
            }

            return model;
        }

        /// <summary>
        /// 压入文件流
        /// </summary>
        /// <param name="model"></param>
        private void Push(T model)
        {
            //Console.WriteLine(JsonConvert.SerializeObject(model));
            //Console.WriteLine($"Push {model.Id} {model.Timestamp}");

            var file_info = GetModelFileInfo();
            var model_byte = GetModelByte(model);

            foreach (var property in file_info.PropertyInfos)
            {
                if (property.Property.PropertyType != typeof(string))
                    continue;

                var data = StreamEx.GetByteBySimplePackingData(property.Property.GetValue(model));
                var position = DataStream.Write(data);

                var position_byte = StreamEx.GetByteBySimplePackingData(position);
                Array.Copy(position_byte, 0, model_byte, property.Offset, position_byte.Length);
            }

            IndexStream.Write(model_byte);

#if DEBUG
            // 验证当前位置是否合理
            if (IndexStream.Position % file_info.Length != 0)
                throw new Exception($"写入之后位置错误 {IndexStream.Position}");
#endif
        }

        public QueryList<T> GetAllModelFromFile(Stream stream)
        {
            stream.Seek(0, SeekOrigin.Begin);
            var head_info = stream.Read<MergeFileHeadInfo>();

            if (head_info.ModelTypeHashCode != typeof(T).GetType().GetHashCode())
                throw new Exception("模型的HashCode不匹配");

            var model_file_info = GetModelFileInfo();

            Console.WriteLine("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
            Console.WriteLine("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");

            QueryList<T> list = new QueryList<T>();
            list.Start = head_info.StartTimeStamp;
            list.Stop = head_info.StopTimeStamp;
            MergeFileInfo[] file_infos = new MergeFileInfo[head_info.FileCount];
            for (int i = 0; i < file_infos.Length; ++i)
            {
                file_infos[i] = stream.Read<MergeFileInfo>();

                Console.WriteLine($"{i} =================");
                foreach (var item in typeof(MergeFileInfo).GetProperties())
                {
                    Console.WriteLine($"{item.Name}\t{item.GetValue(file_infos[i])}");
                }
            }

            if (file_infos.Any(n => n.IndexFileLength % model_file_info.Length != 0))
                throw new Exception("错误的文件格式");

            var skip_hashcode = 0;

            foreach (var file_info in file_infos)
            {
                //stream.Seek(file_info.IndexFilePosition, SeekOrigin.Begin);
                //if (stream.Read<Int32>() != head_info.ModelTypeHashCode)
                //    throw new Exception("错误的 索引 Hash标识");

                //stream.Seek(file_info.DataFilePosition, SeekOrigin.Begin);
                //if (stream.Read<Int32>() != head_info.ModelTypeHashCode)
                //    throw new Exception("错误的 数据 Hash标识");

                // 计算理论数量
                var model_count = (file_info.IndexFileLength - skip_hashcode) / model_file_info.Length;
                var model_byte = new byte[model_file_info.Length];
                for (int i = 0; i < model_count; i++)
                {
                    stream.Seek(file_info.IndexFilePosition + skip_hashcode + model_byte.Length * i, SeekOrigin.Begin);

                    int sum = 0;
                    while (true)
                    {
                        sum += stream.Read(model_byte, sum, model_byte.Length - sum);
                        if (sum >= model_byte.Length)
                            break;
                    }

                    var model = GetByteModel(model_byte);

                    try
                    {
                        // 还原字符串数据
                        foreach (var property_info in model_file_info.PropertyInfos)
                        {
                            if (property_info.Property.PropertyType != typeof(string))
                                continue;

                            var position = BitConverter.ToInt64(model_byte.Sub(property_info.Offset, property_info.Length), 0);
                            stream.Seek(file_info.DataFilePosition + skip_hashcode + position - file_info.DataFileOffset, SeekOrigin.Begin);

                            var length = stream.Read<Int32>();
                            var str = Encoding.UTF8.GetString(stream.Read(length));

                            property_info.Property.SetValue(model, str);
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                        Console.WriteLine(ex.StackTrace);
                    }

                    if (model != null)
                        list.Items.Add(model);
                }
            }

            return list;
        }

        /// <summary>
        /// 搜索信息
        /// </summary>
        /// <param name="index_stream"></param>
        /// <param name="data_stream"></param>
        /// <param name="start_time"></param>
        /// <param name="stop_time"></param>
        /// <returns></returns>
        public SearchInfo Search(Stream index_stream, Stream data_stream, long start_time, long stop_time)
        {
            if (index_stream.Length == 0)
                return new SearchInfo();

            var model_file_info = GetModelFileInfo();

            if (index_stream.Length % model_file_info.Length != 0)
                throw new Exception("错误的数据长度");

            var search_info = new SearchInfo();

            search_info.IndexStartIndex = SearchIndex(index_stream, start_time, true);
            search_info.IndexStopIndex = SearchIndex(index_stream, stop_time, false);

            index_stream.Seek(search_info.IndexStartIndex, SeekOrigin.Begin);
            var start_byte = index_stream.Read(model_file_info.Length);
            var start_model = GetByteModel(start_byte);
            int? start_position = null;
            var first_property = model_file_info.PropertyInfos?.FirstOrDefault(n => n.Property.PropertyType == typeof(string));
            if (first_property != null && (start_position = StructHelper.ByteToStruct<int>(start_byte.Sub(first_property.Offset, first_property.Length))).HasValue)
                search_info.DataStartIndex = start_position.Value;


            index_stream.Seek(search_info.IndexStopIndex - model_file_info.Length, SeekOrigin.Begin);
            var stop_byte = index_stream.Read(model_file_info.Length);
            var stop_model = GetByteModel(stop_byte);
            int? stop_position = null;
            var last_property = model_file_info.PropertyInfos?.LastOrDefault(n => n.Property.PropertyType == typeof(string));
            if (last_property != null && (stop_position = StructHelper.ByteToStruct<int>(stop_byte.Sub(last_property.Offset, last_property.Length))).HasValue)
            {
                data_stream.Seek(stop_position.Value, SeekOrigin.Begin);
                var str_length = data_stream.Read<Int32>();
                search_info.DataStopIndex = str_length + sizeof(Int32) + stop_position.Value;
            }

            return search_info;
        }

        /// <summary>
        /// 搜索数据流
        /// </summary>
        /// <param name="index_stream">索引文件，纯索引文件</param>
        /// <param name="time"></param>
        /// <param name="is_head">是否是头模式：是 找到符合条件的第一个；否 找到符合条件的最后一个</param>
        /// <returns></returns>
        private long SearchIndex(Stream index_stream, long time, bool is_head)
        {
            var model_file_info = GetModelFileInfo();

            if (index_stream.Length % model_file_info.Length != 0)
                throw new Exception("错误的数据长度");

            var count = index_stream.Length / model_file_info.Length;

            if (count == 0)
                return 0;

            long LastSearchStartIndex = 0;
            long LastSearchStopIndex = count - 1;

            var first_model = GetModelFromStringByPosition(index_stream, GetModelOffset(LastSearchStartIndex));
            var last_model = GetModelFromStringByPosition(index_stream, GetModelOffset(LastSearchStopIndex));

            if (time < first_model.Timestamp)
                return GetModelOffset(LastSearchStartIndex);

            if (time > last_model.Timestamp)
                return GetModelOffset(LastSearchStopIndex + 1);

            /*  二分法搜索合适的位置
             *      上一条数据不合适，并且本条数据合适的情况
             *      上次搜索
             */

            long middle = 0;
            while (true)
            {
                long length = LastSearchStopIndex - LastSearchStartIndex + 1;
                if (length == 0)
                    return LastSearchStartIndex;

                if (length > 2)
                {
                    middle = (length / 2 + LastSearchStartIndex);
                    if (IsHere(index_stream, time, middle, is_head))
                        return GetModelOffset(middle + (is_head ? 0 : 1));
                }
                else
                {
                    if (IsHere(index_stream, time, LastSearchStartIndex, is_head))
                        return GetModelOffset(LastSearchStartIndex + (is_head ? 0 : 1));

                    if (IsHere(index_stream, time, LastSearchStopIndex, is_head))
                        return GetModelOffset(LastSearchStopIndex + (is_head ? 0 : 1));
                }

                T model = GetModelFromStringByPosition(index_stream, GetModelOffset(middle));

                if (is_head)
                {
                    // 查找第一个
                    if (model.Timestamp < time)
                    {
                        LastSearchStartIndex = middle;
                    }
                    else
                    {
                        LastSearchStopIndex = middle;
                    }
                }
                else
                {
                    // 查找最后一个
                    if (model.Timestamp > time)
                    {
                        LastSearchStopIndex = middle;
                    }
                    else
                    {
                        LastSearchStartIndex = middle;
                    }
                }
            }
        }

        private bool IsHere(Stream index_stream, long time, long middle, bool is_head)
        {
            if (index_stream.Length == 0 && middle == 0)
                return true;

            if (is_head && middle == 0)
                return true;

            if (!is_head && middle == (index_stream.Length / GetModelFileInfo().Length) - 1)
                return true;

            T model = GetModelFromStringByPosition(index_stream, GetModelOffset(middle));

            if (is_head)
            {
                T last = GetModelFromStringByPosition(index_stream, GetModelOffset(middle - 1));
                if (last.Timestamp < time && model.Timestamp >= time)
                    return true;
            }
            else
            {
                T next = GetModelFromStringByPosition(index_stream, GetModelOffset(middle + 1));
                if (next.Timestamp > time && model.Timestamp <= time)
                    return true;
            }

            return false;
        }

        private T GetModelFromStringByPosition(Stream stream, long position)
        {
            stream.Seek(position, SeekOrigin.Begin);
            var model_file_info = GetModelFileInfo();
            return GetByteModel(stream.Read(model_file_info.Length));
        }

        private long GetModelOffset(long i)
        {
            return GetModelFileInfo().Length * i;
        }

        /// <summary>
        /// 获取索引流
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        private Stream GetIndexStream(long time)
        {
            Stream stream = null;

            var file_path = GetSaveFilePath(StreamType.Index, time);
            if (File.Exists(file_path))
            {
                // TODO bianchx: 文件已经存在，需要判定文件写入情况，如果软件崩溃很有可能最后写入数据不全

                stream = new FileStream(file_path, FileMode.Append);
                Console.WriteLine($"连接之前的文件流 {file_path}");
            }
            else
            {
                Console.WriteLine("创建文件流");
                stream = new FileStream(file_path, FileMode.CreateNew);
            }

            return stream;
        }

        /// <summary>
        /// 获取数据流
        /// </summary>
        /// <param name="file_time"></param>
        /// <returns></returns>
        private Stream GetDataStream(long file_time)
        {
            Stream stream = null;

            var file_path = GetSaveFilePath(StreamType.Data, file_time);
            if (File.Exists(file_path))
            {
                // TODO bianchx: 文件已经存在，需要判定文件写入情况，如果软件崩溃很有可能最后写入数据不全

                stream = new FileStream(file_path, FileMode.Append);
            }
            else
            {
                stream = new FileStream(file_path, FileMode.CreateNew);
            }

            return stream;
        }

        /// <summary>
        /// 获取保存文件路径
        /// </summary>
        /// <param name="stream_type"></param>
        /// <param name="timestamp"></param>
        /// <returns></returns>
        public string GetSaveFilePath(StreamType stream_type, long timestamp)
        {
            return Path.Combine(ModelSerializeHelper.SavePath, $"{typeof(T).Name}_{timestamp}.{stream_type}db");
        }

        /// <summary>
        /// 合并文件
        /// </summary>
        /// <param name="start"></param>
        /// <param name="stop"></param>
        public string MergeFile(long start, long stop)
        {
            Console.WriteLine($"MergeFile {start} {stop}");
            MergeFinshResetEvent.Reset();
            NewModelResetEvent.Set();
            NewModelPushFinsh.WaitOne();

            IndexStream?.Close();
            IndexStream = null;

            DataStream?.Close();
            DataStream = null;

            Console.WriteLine("开始合并，暂停写入");

            var file_path = Path.Combine(ModelSerializeHelper.TempPath, Guid.NewGuid().ToString("N"));

            try
            {
                var start_index = GetFileTimestamp(start);
                var stop_index = GetFileTimestamp(stop);

                if (start_index > stop_index)
                    return string.Empty;

                using (FileStream fs = new FileStream(file_path, FileMode.Create))
                {
                    // 写入总描述
                    MergeFileHeadInfo head_info = new MergeFileHeadInfo
                    {
                        FileCount = (int)(stop_index - start_index + 1),
                        ModelTypeHashCode = typeof(T).GetType().GetHashCode(),
                        StartTimeStamp = start,
                        StopTimeStamp = stop,
                    };

                    var head_byte = StructHelper.StructToBytes(head_info);
                    fs.Write(head_byte);

                    var skip_length = head_byte.Length + Marshal.SizeOf<MergeFileInfo>() * head_info.FileCount;
                    long data_sum = 0;

                    // 依次写入分离文件头
                    for (long i = start_index; i <= stop_index; i++)
                    {
                        int try_count = 0;

                        var data_stream = GetMergeDataStream(i);
                        var index_stream = GetMergeIndexStream(i);

                        try
                        {
                            if (try_count > 10)
                                throw new Exception("超过重试次数上限");

                            ++try_count;

                            var index = i - start_index;


                            //var index_offset = 0;
                            //var data_offset = 0;

                            var search_info = Search(index_stream, data_stream, start, stop);

                            MergeFileInfo file_info = new MergeFileInfo
                            {
                                IndexFilePosition = data_sum + skip_length,
                                IndexFileOffset = search_info.IndexStartIndex,
                                IndexFileLength = search_info.IndexStopIndex - search_info.IndexStartIndex,

                                DataFilePosition = data_sum + skip_length + search_info.IndexStopIndex - search_info.IndexStartIndex,
                                DataFileOffset = search_info.DataStartIndex,
                                DataFileLength = search_info.DataStopIndex - search_info.DataStartIndex,
                            };
                            var file_byte = StructHelper.StructToBytes(file_info);

                            Console.WriteLine("===========================================");
                            foreach (var property in typeof(MergeFileInfo).GetProperties())
                            {
                                Console.WriteLine($"{property.Name}\t{property.GetValue(file_info)}");
                            }

                            // 移动到文件描述的合适位置
                            fs.Seek(head_byte.Length + file_byte.Length * index, SeekOrigin.Begin);
                            fs.Write(file_byte);

                            // 写入索引文件
                            fs.Seek(file_info.IndexFilePosition, SeekOrigin.Begin);
                            fs.Write(index_stream, file_info.IndexFileOffset, file_info.IndexFileLength);
                            index_stream.Close();

                            // 写入数据文件
                            fs.Seek(file_info.DataFilePosition, SeekOrigin.Begin);
                            fs.Write(data_stream, file_info.DataFileOffset, file_info.DataFileLength);
                            data_stream.Close();

                            data_sum += file_info.DataFileLength + file_info.IndexFileLength;
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                            Console.WriteLine(ex.StackTrace);
                            Thread.Sleep(200);
                        }
                        finally
                        {
                            index_stream?.Close();
                            data_stream?.Close();
                        }
                    }
                }
            }
            catch (Exception ex)
            {

            }

            Thread.Sleep(1000);
            MergeFinshResetEvent.Set();
            Console.WriteLine("合并完成");

            return file_path;
        }

        /// <summary>
        /// 获取合并索引流
        /// （暂时阻断写入逻辑，完成合并之后继续）
        /// </summary>
        /// <param name="file_time"></param>
        /// <returns></returns>
        private Stream GetMergeIndexStream(long file_time)
        {
            Stream stream = null;
            var file_path = GetSaveFilePath(StreamType.Index, file_time);
            if (File.Exists(file_path))
            {
                stream = new FileStream(file_path, FileMode.Open);
            }
            else
            {
                stream = new FileStream(file_path, FileMode.CreateNew);
            }
            return stream;
        }

        /// <summary>
        /// 获取合并数据流
        /// </summary>
        /// <param name="file_time"></param>
        /// <returns></returns>
        private Stream GetMergeDataStream(long file_time)
        {
            Stream stream = null;
            var file_path = GetSaveFilePath(StreamType.Data, file_time);
            if (File.Exists(file_path))
            {
                stream = new FileStream(file_path, FileMode.Open);
            }
            else
            {
                stream = new FileStream(file_path, FileMode.CreateNew);
            }
            return stream;
        }

        private static long GetFileTimestamp(long time)
        {
            return time / TIME_INTERVAL;
        }
    }
}
